# -*- coding: utf-8 -*-
import logging, gzip, json, tarfile, time, urllib,copy
import hashlib,re
import os, traceback
from flask import Flask, url_for, request, session, send_from_directory, current_app
from flask_security import Security
from flask_security import SQLAlchemyUserDatastore
from flask_security.utils import hash_password
from flask_admin import helpers as admin_helpers
from .configs import Config
from .utils.loggConfigHelper import configure_logging
from .utils.JsonToDatetime import JsonToDatetime
from .utils.helpers import app_config_from_env
from .extensions import db, babel, redis_store, admin
from .models import (Machine, User, Role, NetWorkCard, TaskLog, Idc, Broadbandccount, HardDisk, WarnReceiver, WarnItem,
                     WarnLogs, DefaultNicFlowData, BandWidthLog, BandWidthNineFiveLog, BandWidthHistoryPic,
                     AqyBandWidth95, HdErrorLog, PveVmConf, HelpDoc, Deployment_Log, TimedTasks, SourceAqySnmpLog,
                     SourceMySnmpLog, SourceZabbixSnmpLog)
from .views import (UserView, RoleView, AppConfigView, MachineView, TaskLogView, NetworkCardView, HardDiskView, IdcView,
                    BroadBandAccountView, DefaultNicFlowDataView, AqyBandWidth95View, HdErrorLogView, PveVmConfView,
                    TimedTasksView, SourceMyBandWidth95View, SourceAqyBandWidth95View, CalcBandWidth95View, ShowBandWidth95View)
from .views.warn import WarnReceiverView, WarnLogsView, WarnItemView
from adminApp.api import api_bp, push_api
import warnings
from flask_apscheduler import APScheduler
from apscheduler.schedulers.background import BackgroundScheduler, BlockingScheduler
import threading
from datetime import datetime, timedelta
from sqlalchemy import or_, and_
from adminApp.utils.JsonToDatetime import MyEncoder, JSONEncoder
from adminApp.utils.telnet_util import check_ip_for_telnet




scheduler = APScheduler(BackgroundScheduler(timezone="Asia/Shanghai"))
# scheduler = BlockingScheduler()
logger = logging.getLogger(__name__)
# scheduler = APScheduler()
# 定时任务
"""
1.清理redis 网卡流量数据 每小时一次，保留500条数据  MACHINE_NIC_RATE = "machine:{}:nic:{}:rate"      # 第1个参数 设备ID system-uuid  第二个网卡名称
2.测试设备的8400端口是否畅通
3.检查设备连接状态，并发送邮件或者短信等警告
"""

class SchConfig(object):

    SCHEDULER_API_ENABLED = True


def get_md5(val):
    v = json.dumps(val, cls=JsonToDatetime)
    md = hashlib.md5()
    md.update(v.encode('utf-8'))
    return md.hexdigest()

# 执行定时任务
@scheduler.task('interval', id='exec_timed_tasks', seconds=60)
def exec_timed_tasks():
    print('run exec_timed_tasks {}'.format(datetime.now()))
    try:
        with scheduler.app.app_context():
            # 查询任务
            now=datetime.now()
            uuids = []
            for r in db.session.query(TimedTasks).filter(TimedTasks.status==True).all():
                add_task=False
                try:
                    exids = json.loads(r.target_machine_ids)
                except:
                    exids = dict()

                if re.match('^\d{2,}:\d{2,}$', r.exec_time) and exids and r.command_txt:
                    if now.strftime('%H:%M')==r.exec_time:
                        if not r.last_run:
                            add_task=True
                        else:
                            if now.strftime('%Y-%m-%d')!=r.last_run.strftime('%Y-%m-%d'):
                                add_task=True
                if add_task:
                    for id,uuid in exids.items():
                        if id and uuid:
                            db.session.add(TaskLog(is_sytem_build=True, command_text=r.command_txt, command_type="shell",
                                                   machine_id=id))
                            uuids.append(uuid)
                    r.last_run=now
            db.session.commit()
            for uuid in uuids:
                redis_store.rpush(current_app.config.get('RD_WILL_TASK_ID'), uuid)
    except:
        logger.error("exec_timed_tasks error:{}".format(traceback.format_exc()))

@scheduler.task('interval', id='warn_check', seconds=60)
def warn_check():
    '设备在线状态监测,更新在线状态，发送离线警报, 系统盘空间异常警报'
    '客户程序不能正常运行也会造成设备离线状态'
    try:
        with scheduler.app.app_context():
            warn_adds = []
            # 最后更新时间在当前时间前后15秒内的记录更新为在线
            for r in db.session.query(Machine).all():
                rtx = redis_store.get(current_app.config.get('RD_DEF_NETIO').format(r.system_uuid))
                if rtx:
                    r.is_line = True
                    r.warn_logs.filter(WarnLogs.warn_type=='offline').filter(WarnLogs.status.in_(['wait', 'work'])).update({'status':'over'},synchronize_session=False)
                    try:
                        rtx_data=json.loads(rtx)
                        r.default_nic_rx = rtx_data[0]
                        r.default_nic_tx = rtx_data[1]
                    except:
                        r.default_nic_rx = 0
                        r.default_nic_tx = 0
                else:
                    r.is_line = False
                    r.default_nic_rx = 0
                    r.default_nic_tx = 0
                    warn_adds.append(dict(machine_id=r.id, title='设备掉线', msg="设备离线", type="offline", system_uuid=r.system_uuid))
            # 检测硬盘读写错误，温度，系统盘空间占用大于10G
            for r in db.session.query(Machine.id,HardDisk.name, HardDisk.mountpoint, Machine.system_uuid)\
                    .join(Machine).filter(HardDisk.read_write==False):
                warn_adds.append(dict(machine_id=r.id,title='读写错误', msg="{}-{}读写错误".format(r.name, r.mountpoint), type="read_write_err", system_uuid=r.system_uuid))
            for r in db.session.query(Machine.id,HardDisk.name, HardDisk.temperature, Machine.system_uuid)\
                    .join(Machine).filter(HardDisk.temperature>=60):
                warn_adds.append(dict(machine_id=r.id,title='温度超标', msg="{}-{}度，温度超标".format(r.name, r.temperature), type="temperature_warn", system_uuid=r.system_uuid))
            for r in db.session.query(Machine.id,HardDisk.name, HardDisk.used, HardDisk.total, Machine.system_uuid)\
                    .join(Machine).filter(HardDisk.is_root_disk==True).filter(HardDisk.used>1024*1024*1024*10):
                warn_adds.append(dict(machine_id=r.id,title='空间异常', msg="系统盘{}共{}，使用{}，空间异常".format(r.name,round(r.total/1024/1024/1024,1),round(r.used/1024/1024/1024),1), type="rootdiskexception", system_uuid=r.system_uuid))
            for wm in warn_adds:
                # 更新到警报表，若有同 MD5 的警报信息，且状态是 wait 或 work 则不添加
                result_md5 = get_md5(wm)
                if not db.session.query(WarnLogs).filter(WarnLogs.machine_id == wm['machine_id']) \
                        .filter(WarnLogs.warn_txt_md5 == result_md5) \
                        .filter(WarnLogs.status.in_(['wait', 'work'])).first():
                    # 添加到警报信息
                    rkey="{}:{}".format(current_app.config.get('RD_WARN_CONFIRM').format(wm['system_uuid']),result_md5)
                    warn_times = redis_store.get(rkey)
                    warn_times = int(warn_times.decode()) if warn_times else 0
                    if warn_times >= 5:
                        # 连续三次报警则加入警报日志
                        db.session.add(WarnLogs(status='wait', warn_type=wm['type'], title=wm['title'],
                                                warn_txt=json.dumps(wm, ensure_ascii=False,
                                                                    indent=1,cls=JsonToDatetime),
                                                warn_txt_md5=result_md5, machine_id=wm['machine_id']))
                    else:
                        redis_store.set(rkey, warn_times + 1, ex=600)
            db.session.commit()
    except:
        logger.error("warn_check error:{}".format(traceback.format_exc()))


@scheduler.task('interval', id='test_port', seconds=1800)
def test_port():
    '测试8400 及 加速服务端口畅通'
    def check_port(ip, port, id, drc):
        drc['{}_{}_{}'.format(ip, port, id)] = check_ip_for_telnet(ip, port)

    with scheduler.app.app_context():
        try:
            theads = []
            result = dict()
            for m in db.session.query(Machine).filter(Machine.is_centos==True).filter(Machine.aqy_device_id!=None).filter(Machine.aqy_device_id!='null'):
                try:
                    if m.exportstatus_8400:
                        eps = json.loads(m.exportstatus_8400)
                        ips = eps.get('exportstatus', {}).get('host', [])
                        if ips:
                            for i_p in ips:
                                ip, port = i_p.split(':')
                                t1 = threading.Thread(target=check_port, args=(ip, 8400, m.id, result), daemon=True)
                                t2 = threading.Thread(target=check_port, args=(ip, port, m.id, result), daemon=True)
                                theads.append(t1)
                                theads.append(t2)
                                t1.start()
                                t2.start()
                        else:
                            m.port_8400_chk = 'noip'
                    else:
                        m.port_8400_chk = 'noserver'
                except:
                    m.port_8400_chk = 'error'
                m.port_8400_chk_last = datetime.now()
                m.port_chk_result = None
                m.port_chk_err_num = 0
            db.session.commit()
            for t in theads:
                t.join()
            result2 = dict()
            for k, v in result.items():
                ip, port, id = k.split('_')
                if not result2.get(id):
                    result2[id] = dict(allchk=0, errors=0, errlist=[])
                result2[id]['allchk'] += 1
                if 'failed' == v:
                    result2[id]['errors'] += 1
                    result2[id]['errlist'].append('{}:{}'.format(ip, port))
            for k, v in result2.items():
                db.session.query(Machine).filter(Machine.id == k).update({'port_8400_chk': 'ok' if v.get('allchk')>v.get('errors') else 'failed',
                                                                          'port_chk_result': json.dumps(v),
                                                                          'port_chk_err_num': v.get('errors')})
            db.session.commit()
        except:
            logger.error("test_port err:{}".format(traceback.format_exc()))


# 监控客户端程序文件版本变化,自动更新 client_config.json
@scheduler.task('interval', id='check_mon_client_version', seconds=600)
def check_mon_client_version():
    tp = os.path.abspath('./adminApp/static/monclient.tar.gz')
    if os.path.isfile(tp):
        try:
            # 读取上传的客户端压缩包文件中的配置文件
            with tarfile.open(tp) as ft:
                # 解压客户端配置
                ft.extract('monclient/config.json', path=os.path.abspath('.'))
                with ft.extractfile('monclient/config.json') as f:
                    n_ver = json.loads(f.read()).get('version')
                with scheduler.app.app_context():
                    # 读取需要自动更新的客户端
                    for r in db.session.query(Machine.client_version, Machine.system_uuid, Machine.id).filter(Machine.is_line==True).filter(Machine.is_auto_update_program==True):
                        if r[0] and r[0]!=n_ver:
                            db.session.add(TaskLog(is_sytem_build=True, command_text="auto_update", command_type="mark", machine_id=r[2]))
                            db.session.commit()
                            redis_store.rpush(current_app.config.get('RD_WILL_TASK_ID'), r[1])
        except:
            logger.error(str(traceback.format_exc()))
    else:
        logger.error('客户端程序包不存在')


@scheduler.task('interval', id='send_warn_to_device', seconds=10)
def send_warn_to_device():
    # 读取警信息，并发送到设备
    # print('读取警信息，并发送到设备')
    with scheduler.app.app_context():
        i = 0
        txt = []
        for r in db.session.query(WarnLogs).filter(WarnLogs.status == 'wait') \
                .filter(or_(WarnLogs.send_warns == 0,
                            WarnLogs.last_send_warn < (datetime.now() + timedelta(seconds=-600)))).all():
            i = i + 1
            txt.append("%s-%s" % (r.id, r.warn_type))
            r.last_send_warn=datetime.now()
            r.send_warns =r.send_warns + 1
        if txt:
            # print('发送警告')
            if send_weixin("{}条设备警告".format(i), ','.join(txt)):
                db.session.commit()
            else:
                pass
                # print('发送警告失败')
        else:
            pass
            # print('没有符合要求的警告信息')


def send_weixin(title, content):
    # baseurl = "https://qyapi.weixin.qq.com/cgi-bin/gettoken?corpid=这里填写CorpID&corpsecret=这里填写应用的Secret"
    #        GET https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=APPID&secret=APPSECRET
    weixin_cfg = redis_store.get(current_app.config.get('RD_WEIXIN_CFG'))
    if weixin_cfg:
        try:
            weixin_cfg = json.loads(weixin_cfg)
            baseurl = "https://qyapi.weixin.qq.com/cgi-bin/gettoken?corpid={}&corpsecret={}".format(weixin_cfg.get('CorpID'), weixin_cfg.get('Secret'))
            result = json.loads(urllib.request.urlopen(urllib.request.Request(baseurl)).read())
            access_token = result.get("access_token")
            url = "https://qyapi.weixin.qq.com/cgi-bin/message/send?access_token={0}".format(access_token)
            payload = {
                "touser": "@all",  # 如果你只想发给组内账号为zhangsan的人，这里就填写zhangsan，如果是zhangsan和lisi,那么就写 zhangsan|lisi
                # "toparty":"部门ID",
                "msgtype": "text",
                # "agentid": "这里填写上面Secret对应的agentid",
                "agentid": weixin_cfg.get('AppID'),
                "text": {
                    "content": "title:{0}\n content:{1}".format(title, content)
                },
                "safe": "0"
            }
            data = urllib.parse.urlencode(payload).encode("utf-8")
            # ret = urllib.request.urlopen(url, data=json.dumps(payload, ensure_ascii=False, cls=JsonToDatetime))
            ret = json.loads(urllib.request.urlopen(url, data=data).read())
            # print(ret)
            return True
        except:
            logger.error("send msg to weixin error: {}".format(traceback.format_exc()))
            return False
    else:
        # logger.error("没有微信接收警报配置信息")
        return False


# 将任务加入 redis 队列
def task_to_redis(app):
    while True:
        try:
            with app.app_context():
                bp_data = redis_store.blpop(current_app.config.get('RD_WILL_TASK_ID'))
                r_key, system_uuid = bp_data if bp_data else (None, None)
                if system_uuid:
                    system_uuid= system_uuid.decode() if isinstance(system_uuid, bytes) else system_uuid
                    system_uuid = str(system_uuid)
                    result = []
                    for ro in db.session.query(TaskLog).join(Machine).filter(Machine.system_uuid==system_uuid).filter(TaskLog.run_state=='wait').all():
                        result.append(ro.single_to_dict())
                        ro.run_state = 'task_queue'
                    db.session.commit()
                    if result:
                        task_data = json.dumps(result, cls=JsonToDatetime)
                        logger.info('add task to redis: uuid={} data={}'.format(system_uuid, task_data))
                        redis_store.rpush(current_app.config.get('RD_TASK').format(system_uuid), task_data)
                else:
                    logger.info('add task to redis: cant find systemuuid, data={}'.format(bp_data))
        except:
            logger.error("task to redis error: {}".format(traceback.format_exc()))
        time.sleep(0.01)


# 处理客户端发送的数据
def exec_monclient_task_result(app):
    logger.info('exec_monclient_task_result start')
    i=0
    #aqybdcls="Unix,BytesSent,BytesReceived,MaxConn,ActiveOpens,PassiveOpens,CurrEstab,InErrs,InCsumErrors,KCPInErrors,InPkts,OutPkts,InSegs,OutSegs,InBytes,OutBytes,RetransSegs,FastRetransSegs,EarlyRetransSegs,LostSegs,RepeatSegs,FECParityShards,FECErrs,FECRecovered,FECShortShards".split(',')
    aqybdcls="Unix,MbpsReceived,MbpsSent".split(',')
    while True:
        i+=1
        try:
            with app.app_context():
                # 读取客户端发送过来的数据包，进行处理
                bp_data = redis_store.blpop(current_app.config.get('RD_TASK_RESULT'))
                r_key, data = bp_data if bp_data else (None, None)
                if data:
                    data = json.loads(data)
                    # 固定参数
                    # {version=版本, system_uuid=uuid, post_type=post_type, data=推送结果}
                    client_version = data.get('version')
                    system_uuid = data.get('system_uuid')
                    post_type = data.get('post_type')
                    result = data.get('data')
                    client_result = []
                    if system_uuid:
                        # if system_uuid=='4c4c4544-004d-5910-8034-cac04f473432':
                        #     logger.info('exec_monclient_task_result exec data:{}'.format(data))
                        if 'task_result' == post_type:
                            # 服务端推送的 位于 任务表TaskLog的数据结果 更新任务表
                            for k in result:
                                if k.get('command_type')=='mark':
                                    client_result.append(dict(type=k.get('command_text'), data=k.get('result').copy()))
                                _task_log = db.session.query(TaskLog).filter(TaskLog.id==k.get('id')).first()
                                if _task_log:
                                    upd_data = k.copy()
                                    upd_data['result'] = json.dumps(upd_data['result'], indent=1, cls=JsonToDatetime)
                                    del upd_data['id']
                                    _task_log.update_from_dict(False, **upd_data)
                        else:
                            # 客户端固定信息 ['warn', 'system_info']
                            client_result.append(dict(type=post_type, data=result))
                        # 检测数据库中是否存在system_uuid
                        _machine = db.session.query(Machine).filter(Machine.system_uuid==system_uuid).one_or_none()
                        if _machine is None:
                            # 若不存在，则添加
                            logger.info('exec_monclient_task_result systemuuid:{} not find go add'.format(system_uuid))
                            _machine=Machine(system_uuid=system_uuid, client_version=client_version)
                            # 添加获取系统信息任务
                            _machine.task_logs.append(
                                TaskLog(is_sytem_build=True, command_text="system_info", command_type="mark"))
                            db.session.add(_machine)
                            db.session.commit()
                            redis_store.rpush(current_app.config.get('RD_WILL_TASK_ID'), system_uuid)
                            redis_store.set(current_app.config.get('RD_MACHINE_EXISTS').format(system_uuid), 1)
                        for k in client_result:
                            ex_type=k.get('type')
                            ex_d=k.get('data')
                            if not ex_d:
                                continue
                            if 'heart_beat'==ex_type:
                                # 处理推送的默认网卡流量信息
                                # print(ex_d)
                                _machine.default_nic_rx = ex_d.get('default_nic_rx_tx',[0,0])[0]
                                _machine.default_nic_tx = ex_d.get('default_nic_rx_tx',[0,0])[1]
                            elif 'warn' == ex_type:
                                # 更新到警报表，若有同 MD5 的警报信息，且状态是 wait 或 work 则不添加
                                result_md5 = get_md5(ex_d)
                                same_warn = _machine.warn_logs.filter(WarnLogs.warn_txt_md5==result_md5).filter(WarnLogs.status.in_(['wait', 'work'])).scalar()
                                if not same_warn:
                                    # 添加到警报信息
                                    _machine.warn_logs.append(WarnLogs(status='wait', title=ex_d.get('title'),
                                                                       warn_txt=json.dumps(ex_d, indent=1, cls=JsonToDatetime),
                                                                       warn_txt_md5=result_md5))
                            elif 'system_info' == ex_type:
                                # 更新设备信息
                                pve_vm_conf = ex_d.pop('pve_vm_conf',[])
                                hd_info = ex_d.pop('hd_info', [])
                                _machine.update_from_dict(False, **ex_d)
                                _machine.hard_disks.delete()
                                _machine.pvevmconfes.delete()
                                for h in hd_info:
                                    h['read_write'] = True
                                    for hdk,hdv in h.get('mountpoint',{}).items():
                                        if hdv.get('read_write') and hdv.get('read_write')==False:
                                            h['read_write'] = False
                                    _machine.hard_disks.append(HardDisk(**h))
                                for h in pve_vm_conf:
                                    _machine.pvevmconfes.append(PveVmConf(**h))
                            elif 'aqy_bandwidth_95' == ex_type:
                                if _machine.is_centos:
                                    ex_d = [ex_d] if isinstance(ex_d,str) else ex_d
                                    for line in ex_d:
                                        tmabd=line.strip().split(',')
                                        if len(tmabd)==len(aqybdcls):
                                            if not _machine.aqyBandWidth95s.filter(AqyBandWidth95.Unix == tmabd[0]).all():
                                                d95 = dict(zip(aqybdcls, tmabd))
                                                try:
                                                    d95['Unix_time']= datetime.strptime(tmabd[0], '%Y-%m-%d_%H:%M:%S')
                                                except:
                                                    pass
                                                _machine.aqyBandWidth95s.append(AqyBandWidth95(**d95))
                                        else:
                                            logger.error("bd95:{}-len {}-{} -data:{}-".format(_machine.system_uuid, len(tmabd),len(aqybdcls), ex_d))
                            elif 'dif_upd_bandwidth95' == ex_type:
                                if ex_d:
                                    curr_uix = {r[0]:'' for r in db.session.query(AqyBandWidth95.Unix).filter(AqyBandWidth95.machine_id==_machine.id).all()}
                                    for line in ex_d:
                                        tmabd = line.strip().split(',')
                                        if len(tmabd) == len(aqybdcls):
                                            if tmabd[0] not in curr_uix:
                                                d95 = dict(zip(aqybdcls, tmabd))
                                                try:
                                                    d95['Unix_time'] = datetime.strptime(tmabd[0], '%Y-%m-%d_%H:%M:%S')
                                                except:
                                                    pass
                                                _machine.aqyBandWidth95s.append(AqyBandWidth95(**d95))
                            elif 'getsnmp201231' == ex_type:
                                # 获取客户端完整的爱奇艺snmplog 和自己的snmplog
                                # task['result'] = dict(aqysnmp=self.get_aqy_snmp_log(), mysnmp=self.get_my_snmp_log())
                                # 爱奇艺[[unix,BytesSent,OutBytes],[]..]
                                # 我的 [[unix,inBytes,outBytes],[2020-11-16_13:17:53,2(receive),125(sent)]] 前期使用的是转换为bps 后期使用的是Bytes 需要判断在什么时候进行的转换
                                if ex_d:
                                    logger.error("保存{}-snmplog 开始".format(_machine.system_uuid))
                                    wf_path = os.path.join(current_app.config.get('LOG_PATH'),'snmplogs','{}.json'.format(_machine.system_uuid))
                                    wpath, filename = os.path.split(wf_path)
                                    if not os.path.isdir(wpath):
                                        os.makedirs(wpath)
                                    with open(wf_path,'w') as f:
                                        json.dump(ex_d,f)
                                    logger.error("保存{}-snmplog 结束".format(_machine.system_uuid))
                                    # if ex_d.get('aqysnmp'):
                                    #     logger.info("插入 aqy -snmplog 开始")
                                    #     for l in ex_d.get('aqysnmp'):
                                    #         _machine.source_aqy_snmp_logs.append(
                                    #             SourceAqySnmpLog(Unix=datetime.strptime(l[0], '%Y-%m-%d_%H:%M:%S'),
                                    #                              BytesSent=l[1], OutBytes=l[2]))
                                    #     logger.info("插入 aqy -snmplog 结束")
                                    # else:
                                    #     logger.error("获取客户端发送的爱奇艺snmplog数据，字典为空")
                                    # if ex_d.get('mysnmp'):
                                    #     logger.info("插入 mysnmp -snmplog 开始")
                                    #     for l in ex_d.get('mysnmp'):
                                    #         _machine.source_my_snmp_logs.append(
                                    #             SourceMySnmpLog(Unix=datetime.strptime(l[0], '%Y-%m-%d_%H:%M:%S'),
                                    #                              BytesReceive=l[1], OutBytes=l[2]))
                                    #     logger.info("插入 mysnmp -snmplog 结束")
                                    # else:
                                    #     logger.error("获取客户端发送的自己的snmplog数据，字典为空")
                                else:
                                    logger.error("获取客户端发送snmplog数据，字典为空")

                                pass
                            elif 'uninstall' == ex_type:
                                # 卸载客户端程序
                                db.session.query(Machine).filter(Machine.system_uuid==system_uuid).delete()
                                redis_store.delete(current_app.config.get('RD_TASK').format(system_uuid))
                                redis_store.delete(current_app.config.get('RD_DEF_NETIO').format(system_uuid))
                                redis_store.delete(current_app.config.get('RD_MACHINE_MARK_SYSTEM').format(system_uuid))
                        db.session.commit()
                    else:
                        logger.error('exec_monclient_task_result cant find uuid')
                        print('cant find uuid')
                else:
                    logger.info('exec_monclient_task_result data is None: {}'.format(data))
        except:
            logger.error('exec_monclient_task_result error: {}'.format(traceback.format_exc()))
        time.sleep(0.005)


def create_app(config=None):
    app = Flask(__name__)
    appconfig = Config.get(config)
    app.config.from_object(appconfig)
    print('use config {}'.format(config))
    app_config_from_env(app, "NOVEL_DATA")
    configure_logging(app)
    app.config.from_object(SchConfig())
    # logger.info("使用配置:{}".format(config))
    configure_extensions(app)
    import flask_admin_toggle_control
    flask_admin_toggle_control.init_static_ep(app)
    from flask_gzip import Gzip
    Gzip(app)
    scheduler.init_app(app=app)
    scheduler.start()
    push_api.init_app(app)
    app.json_encoder = JSONEncoder
    # socketio.init_app(app)
    threading.Thread(target=exec_monclient_task_result, args=(app,), daemon=True).start()
    threading.Thread(target=task_to_redis, args=(app,), daemon=True).start()

    # @app.route("/book_rar_download/<path:filename>", methods=('POST',))
    # def downloader(filename):
    #     token = request.form.get('token')
    #     if db.session.query(RecevieDomain.id).filter(RecevieDomain.token == token).scalar() is None:
    #         return '', 201
    #     return send_from_directory(appconfig.DATA_GZ_PATH, filename, as_attachment=True)
    return app


def create_backgroud_app(config=None):
    app = Flask(__name__)
    appconfig = Config.get(config)
    app.config.from_object(appconfig)
    print('use config {}'.format(config))
    app_config_from_env(app, "NOVEL_DATA")
    configure_logging(app)
    app.config.from_object(SchConfig())
    # logger.info("使用配置:{}".format(config))
    configure_extensions(app)
    import flask_admin_toggle_control
    flask_admin_toggle_control.init_static_ep(app)
    app.json_encoder = JSONEncoder
    return app

def configure_extensions(app):
    db.init_app(app)
    # compress.init_app(app)
    redis_store.init_app(app)
    admin.init_app(app)
    with warnings.catch_warnings():
        warnings.filterwarnings('ignore', 'Fields missing from ruleset', UserWarning)
        admin.add_view(UserView(User, db.session, name="用户管理", category="系统管理"))
    admin.add_view(RoleView(Role, db.session, name="角色管理", category="系统管理"))
    admin.add_view(IdcView(Idc, db.session, name="机房管理", category="设备管理"))
    admin.add_view(MachineView(Machine, db.session, name="设备管理", category="设备管理"))
    # admin.add_view(NetworkCardView(NetWorkCard, db.session, name="网卡管理", category="设备管理"))
    admin.add_view(HardDiskView(HardDisk, db.session, name="硬盘管理", category="设备管理"))
    admin.add_view(TaskLogView(TaskLog, db.session, name="任务管理", category="设备管理"))
    # admin.add_view(AqyBandWidth95View(AqyBandWidth95, db.session, name="带宽速率点", category="设备管理"))
    admin.add_view(CalcBandWidth95View(name='95值计算', endpoint='calcbandwidth95', category='设备管理'))
    admin.add_view(ShowBandWidth95View(name='速率图查看', endpoint='showbandwidth95', category='设备管理'))
    # admin.add_view(SourceAqyBandWidth95View(SourceAqySnmpLog, db.session, name="AqySource带宽速率值", category="设备管理"))
    # admin.add_view(SourceMyBandWidth95View(SourceMySnmpLog, db.session, name="MySource带宽速率值", category="设备管理"))
    admin.add_view(HdErrorLogView(HdErrorLog, db.session, name="错误硬盘", category="设备管理"))
    admin.add_view(PveVmConfView(PveVmConf, db.session, name="PVE管理", category="设备管理"))
    admin.add_view(TimedTasksView(TimedTasks, db.session, name="定时任务", category="设备管理"))
    #admin.add_view(DefaultNicFlowDataView(DefaultNicFlowData, db.session, name="默认网卡流量记录", category="设备管理"))
    # admin.add_view(BroadBandAccountView(Broadbandccount, db.session, name="宽带账号"))
    # admin.add_view(WarnItemView(WarnItem, db.session, name="报警项目", category="报警管理"))
    admin.add_view(WarnLogsView(WarnLogs, db.session, name="报警日志", category="报警管理"))
    # admin.add_view(WarnReceiverView(WarnReceiver, db.session, name="接收配置", category="报警管理"))
    admin.add_view(AppConfigView(name='接收配置', endpoint='weixinconfig', category='报警管理'))


    user_datastore = SQLAlchemyUserDatastore(db, User, Role)
    security = Security(app, user_datastore)
    babel.init_app(app=app)


    @security.context_processor
    def security_context_processor():
        return dict(
            admin_base_template=admin.base_template,
            admin_view=admin.index_view,
            h=admin_helpers,
            get_url=url_for
        )


def init_admin_user(app):
    from sqlalchemy.sql import exists, func
    import random
    with app.app_context():
        user_role = db.session.query(Role).filter(Role.name == 'user').first()
        super_user_role = db.session.query(Role).filter(Role.name == 'superuser').first()
        isAdd = False
        if not user_role:
            user_role = Role(name='user')
            db.session.add(user_role)
        if not super_user_role:
            isAdd = True
            super_user_role = Role(name='superuser')
            db.session.add(super_user_role)
        db.session.commit()

        # 查询是否有超级管理员
        if db.session.query(func.count('*')).filter(User.active == True).filter(
                User.roles.any(name='superuser')).scalar() < 1:
            insert_name = 'admin'
            insert_pwd = 'admin'
            while True:
                admin_user = db.session.query(User).filter(User.email == insert_name).first()
                if not admin_user:
                    user_datastore = SQLAlchemyUserDatastore(db, User, Role)
                    admin_user = user_datastore.create_user(name=insert_name, email=insert_name,
                                                            password=hash_password(insert_pwd),
                                                            roles=[user_role, super_user_role])
                    db.session.commit()
                    print('新建超级管理员：{} 密码：{}'.format(insert_name, insert_pwd))
                    break
                else:
                    # re build user namep
                    insert_name = ''.join(random.sample('abcdefghijklmnopqrstuvwxyz', 5))
    return




